Albin’s Portfolio Analysis

Author

Abdullah Mahmood

Published

August 8, 2025

(as of 8th August, 2025)

Code
import sys
from pathlib import Path

sys.path.append(str(Path.cwd().parent))
import config
from src import data_ingestion
from src.symbols import Symbols
from src.portfolio import Portfolio
from src.benchmark import Benchmark

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

0.1 Transaction Data

Code
log_files = [
    config.TRANS_LOG_DIR / "albin_trans_log.csv",
]

master_log = data_ingestion.create_master_log(log_files).set_index("Date")
display(master_log.head())
Type Symbol Quantity Price Amount Commission Currency Description Exchange Source
Date
2024-03-11 buy 0P0000GB48.BO 26.17 95.53 -2499.88 0.0 INR SIP BSE Mutual Fund Account
2024-03-11 Net Deposit NaN NaN NaN 2499.88 NaN INR Deposit for SIP India Market Mutual Fund Account
2024-03-18 Net Deposit NaN NaN NaN 2499.88 NaN INR Deposit for SIP India Market Mutual Fund Account
2024-03-18 buy 0P0001QYX3.BO 160.04 15.62 -2499.88 0.0 INR SIP BSE Mutual Fund Account
2024-04-10 buy 0P0000GB48.BO 25.65 97.47 -2499.88 0.0 INR SIP BSE Mutual Fund Account

1 Symbols

List of all identified symbols in the transaction log:

Code
symbol_manager = Symbols(master_log)

symbol_manager.assess()

print("✅ The following symbols were automatically identified:")
found_df = symbol_manager.get_found()

symbol_df = symbol_manager.get_unified_df()
display(found_df[['Name','Type', 'Industry', 'Sector', 'Country', 'Currency']])
✅ The following symbols were automatically identified:
Name Type Industry Sector Country Currency
Symbol
0P0000GB48.BO ICICI Prudential Large Cap Gr mutualfund None None None INR
0P0001QYX3.BO HDFC Defence Reg IDCW-R mutualfund None None None INR
0P00005WE5.BO Nippon India Value Fund mutualfund None None None INR
0P0001QR5R.BO ICICI Prudential Innovation Fund mutualfund None None None INR
0P0000TFYS.BO Nippon India Gold Savings Fund mutualfund None None None INR
AVANTIFEED.NS Avanti Feeds Limited equity Packaged Foods Consumer Defensive India INR
TATASTEEL.NS Tata Steel Limited equity Steel Basic Materials India INR
FEDERALBNK.NS The Federal Bank Limited equity Banks - Regional Financial Services India INR
JYOTHYLAB.NS Jyothy Labs Limited equity Household & Personal Products Consumer Defensive India INR
IRCTC.NS Indian Railway Catering & Tourism Corporation ... equity Railroads Industrials India INR
0P0001784C.BO Tata Digital India Reg Gr mutualfund None None None INR
IDEA.NS Vodafone Idea Limited equity Telecom Services Communication Services India INR
JIOFIN.BO Jio Financial Services Limited equity Asset Management Financial Services India INR
ONGC.BO Oil and Natural Gas Corporation Limited equity Oil & Gas Integrated Energy India INR
GOLDIETF.NS None equity None None None INR
GATECH.NS GACM Technologies Limited equity Asset Management Financial Services India INR
JPPOWER.NS Jaiprakash Power Ventures Limited equity Utilities - Independent Power Producers Utilities India INR
ITC.NS ITC Limited equity Tobacco Consumer Defensive India INR

2 Portfolio Analysis

Code
start_date, end_date, date_range, last_market_day = config.project_dates(
    master_log.index
)

portfolio = Portfolio(master_log, symbol_manager, date_range, last_market_day)
portfolio.calculate_holdings_and_value()
portfolio_value = portfolio.get_total_value_history()

2.1 Current Holdings

Code
portfolio.get_current_holdings().round(2).sort_values(
    "Market Value (INR)", ascending=False
)
Shares Market Value (INR)
0P0000GB48.BO 367.99 40132.99
0P0000TFYS.BO 700.58 27147.97
0P0001784C.BO 104.34 4736.64
TATASTEEL.NS 28.00 4470.76
JIOFIN.BO 10.00 3253.00
IRCTC.NS 4.00 2908.60
GOLDIETF.NS 27.00 2343.60
ONGC.BO 10.00 2337.50
ITC.NS 5.00 2068.00
FEDERALBNK.NS 8.00 1589.04
JPPOWER.NS 6.00 113.64
IDEA.NS 8.00 53.76
GATECH.NS 100.00 51.00

2.2 Realized/Unrealized Returns Summary

Code
portfolio.get_return_summary().round(2)
Income Realized Gains Unrealized Gains Total Return
0P0000TFYS.BO 0.0 0.00 3019.54 3019.54
0P0000GB48.BO 0.0 0.00 2134.81 2134.81
TATASTEEL.NS 0.0 0.00 453.05 453.05
JIOFIN.BO 0.0 0.00 239.87 239.87
JYOTHYLAB.NS 0.0 180.96 0.00 180.96
FEDERALBNK.NS 0.0 0.00 168.76 168.76
IRCTC.NS 0.0 0.00 92.86 92.86
GOLDIETF.NS 0.0 0.00 35.90 35.90
ITC.NS 0.0 0.00 2.74 2.74
IDEA.NS 0.0 0.00 -0.60 -0.60
JPPOWER.NS 0.0 0.00 -0.82 -0.82
GATECH.NS 0.0 0.00 -16.25 -16.25
ONGC.BO 0.0 0.00 -134.94 -134.94
0P0001784C.BO 0.0 0.00 -263.12 -263.12
AVANTIFEED.NS 0.0 -649.99 0.00 -649.99
0P00005WE5.BO 0.0 -868.66 0.00 -868.66
0P0001QR5R.BO 0.0 -1320.27 0.00 -1320.27
0P0001QYX3.BO 0.0 -2309.41 0.00 -2309.41
Code
total_returns = (
    portfolio.holdings["unrealized_gains"]
    + portfolio.holdings["realized_gains"]
    + portfolio.holdings["income"]
)

2.3 Return Contribution per Holding

Code
fig_holdings = go.Figure()

for symbol in total_returns.columns:
    fig_holdings.add_trace(
        go.Scatter(
            x=total_returns.index,
            y=total_returns[symbol],
            mode="lines",
            name=symbol,
            line=dict(width=1),
        )
    )

fig_holdings.update_layout(
    title_text="<b>Individual Holding Performance Over Time</b>",
    template="plotly_white",
    xaxis_title="Date",
    yaxis_title=f"Total Returns ({config.BASE_CURRENCY})",
)

fig_holdings.show()

3 Portfolio Vs. Benchmark Performance

Code
benchmark_simulation = Benchmark(master_log, date_range, last_market_day)
benchmark_simulation.run_simulation()
benchmark_results = benchmark_simulation.get_results()

3.1 Portfolio Vs. Benchmark Market Value & Net Deposits

Code
monthly_deposits = benchmark_results["NetDeposit"].resample("ME").sum()

fig = make_subplots(specs=[[{"secondary_y": True}]])

# Personal Portfolio Value
fig.add_trace(
    go.Scatter(
        x=portfolio_value.index,
        y=portfolio_value,
        mode="lines",
        name="Personal Portfolio",
        line=dict(color="green", width=2),
    ),
    secondary_y=False,
)

# Benchmark Value
fig.add_trace(
    go.Scatter(
        x=benchmark_results.index,
        y=benchmark_results["TotalValue"],
        mode="lines",
        name=f"{config.BENCHMARK_INDEX} Benchmark",
        line=dict(color="red", width=2),
    ),
    secondary_y=False,
)

# Cumulative Net Deposits
fig.add_trace(
    go.Scatter(
        x=benchmark_results.index,
        y=benchmark_results["NetDeposit"].cumsum(),
        mode="lines",
        name="Cumulative Net Deposits",
        line=dict(color="darkgrey", width=1, dash="dash"),
    ),
    secondary_y=False,
)

# Monthly Deposits/Withdrawals (on secondary y-axis)
fig.add_trace(
    go.Bar(
        x=monthly_deposits.index,
        y=monthly_deposits,
        name="Deposits / Withdrawals",
        marker_color="royalblue",
        opacity=0.4,
    ),
    secondary_y=True,
)

fig.update_layout(
    title_text=f"Portfolio Performance vs. {config.BENCHMARK_INDEX} Benchmark",
    template="plotly_white",
    barmode="relative",
    legend=dict(orientation="h"),
    xaxis=dict(
        title="Date",
        type="date"
    ),
)

fig.update_yaxes(
    title_text=f"<b>Portfolio Value ({config.BASE_CURRENCY})</b>", secondary_y=False
)
fig.update_yaxes(
    title_text="<b>Monthly Cash Flow</b>",
    secondary_y=True,
    showgrid=False,
    layer="below traces",
)

fig.show()

3.2 Portfolio Vs. Benchmark Monthly Income

Code
portfolio_income = portfolio.get_monthly_income()
benchmark_income = benchmark_simulation.get_monthly_income()

fig_income_comp = go.Figure()

fig_income_comp.add_trace(
    go.Bar(
        x=portfolio_income.index,
        y=portfolio_income,
        name="Portfolio Income",
        marker_color="mediumseagreen",
    )
)

fig_income_comp.add_trace(
    go.Bar(
        x=benchmark_income.index,
        y=benchmark_income,
        name=f"{config.BENCHMARK_INDEX} Benchmark Income",
        marker_color="grey",
    )
)

fig_income_comp.update_layout(
    title_text="<b>Monthly Income Comparison</b>",
    template="plotly_white",
    barmode="group",
    xaxis_title="Date",
    yaxis_title=f"Net Income ({config.BASE_CURRENCY})",
    legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
)

fig_income_comp.show()